package com.cheng.gateway;

import lombok.Data;

import java.util.Arrays;

/**
 * 我们可以统计一段时间内的请求数量（成功、失败、超时）的占比，当成功率低于80%就开启熔断，
 * 开启一段时间后切换到半开半闭的状态，将少量的流量打入到下游服务，观察是否可用（时间是连续的），随着时间的推移将统计的数据随时进行计算来判断是否开启熔断。
 * 计数器 VS 滑动窗口：
 *          计数器算法是最简单的算法，可以看成是滑动窗口的低精度实现。滑动窗口由于需要存储多份的计数器（每一个格子存一份），所以滑动窗口在实现上需要更多的存储空间。
 *          也就是说，如果滑动窗口的精度越高，需要的存储空间就越大。
 * @author cheng-qiang
 * @date 2022年08月29日14:52
 */
@Data
public class 滑动窗口算法 {
    /**滑动窗口(偏移量)---样本量**/
    private volatile int size = 10;
    /**数据最大值**/
    private static int maxSize = 60;
    /**当前数据长度**/
    private volatile int dataLength = 0;
    /**数组**/
    private volatile Bucket[] data = new Bucket[60];
    /**数组头部**/
    private int head;
    /**数组尾部**/
    private int tail;

    /**
     * 大致思路
     * @author cheng-qiang
     * @date 2022/8/29 14:56
     */
    public void addBucket(Bucket bucket) {
        if (++dataLength > maxSize) {
            resetTailHead();
        } else {
            incrementTail();
        }
        data[tail] = bucket;
    }

    /**
     * 当数据大小不超过最大数据时
     * @author cheng-qiang
     * @date 2022/8/29 15:00
     */
    public void incrementTail() {
        if (dataLength >= size) {
            head = dataLength - size;
        }
        tail = dataLength - 1;
    }

    /**
     * 当数据大小超过最大数据时
     * @author cheng-qiang
     * @date 2022/8/29 15:01
     */
    public void resetTailHead() {
        if (head == maxSize - 1) {
            head = 0;
            dataLength = 10;
            tail++;
        } else {
            head++;
            tail = head + size - 1 - maxSize;
        }
    }

    /**
     * 获取数组锁定区间windowStart[71-80]
     * @author cheng-qiang
     * @date 2022/8/29 15:07
     * @return com.cheng.gateway.Bucket[]
     */
    public Bucket[] getResultData() {
        Bucket[] result = new Bucket[10];
        if (tail > head) {
            for (int i = head, j = 0; i <= tail; i++, j++) {
                result[j] = data[i];
            }
        } else {
            for (int i = head, j = 0; i < maxSize; i++, j++) {
                result[j] = data[i];
            }
            for (int i = 0, j = result.length - 1; i <= tail; i++, j++) {
                result[j] = data[i];
            }
        }
        return result;
    }

    public static void main(String[] args) throws InterruptedException {
        滑动窗口算法 bucketArray = new 滑动窗口算法();
        for (int i = 0; i < 81; i++) {
            Bucket bucket = new Bucket(i);
            bucketArray.addBucket(bucket);
            Thread.sleep(100L);
        }
        System.out.println(Arrays.toString(bucketArray.getData()));
        System.err.println(Arrays.toString(bucketArray.getResultData()));
    }

}
